<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Title</title>
    <style>
      .list-wrap {
        position: relative;
        overflow-y: scroll;
        width: 200px;
        margin: 100px auto;
        box-sizing: border-box;
        border: solid 1px red;
      }
      /* .list-wrap::-webkit-scrollbar {
          display: none;
        } */

      .list {
        position: absolute;
        top: 0;
        left: 0;
      }

      .list li {
        border: solid 1px blue;
        line-height: 30px;
      }
    </style>
  </head>

  <body>
    <!-- 
    虚拟列表的基本实现思路:
      只渲染指定区间的指定数量的部分数据列表: 
        start: 0
        end: 10
        list: 所有数据的大列表
        showList: 根据start和end计算出要显示的小列表
      滚动显示对应的列表
        将外层div的高度指定为一页数据列表的高度
        定义子元素, 指定其高度为总数据的高度, 用于形成一个有很多数据的滚动效果
        绑定外层div滚动监听
          根据滚动高度, 计算出新的start与end值
          那list就会根据最新的start和end进行计算
          指定列表元素指定到当前滚动处: 将其top值指定为滚动的top值
      优化: 利用lodash对滚动回调进行节流处理
    -->
    <ul id="app">
      <div class="list-wrap" ref="listWrap" @scroll="scrollListener">
        <ul class="list" ref="list">
          <li v-for="val in showList">{{val}}</li>
        </ul>
        <div class="scroll-bar" ref="scrollBar"></div>
      </div>
    </ul>
    <script src="https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.21/lodash.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.9/vue.js"></script>
    <script>
      new Vue({
        el: '#app',
        data() {
          return {
            list: [], //超长的显示数据
            itemHeight: 30, //每一列的高度
            showNum: 10, //显示几条数据
            start: 0, //滚动过程显示的开始索引
            end: 10, //滚动过程显示的结束索引
          };
        },
        computed: {
          //显示的数组，用计算属性计算
          showList() {
            return this.list.slice(this.start, this.end);
          },
        },
        mounted() {
          //构造一个超长列表
          for (let i = 0; i < 1000000; i++) {
            this.list.push('列表' + i);
          }
          //计算滚动容器高度
          this.$refs.listWrap.style.height =
            this.itemHeight * this.showNum + 'px';

          //计算总的数据需要的高度，构造滚动条高度
          this.$refs.scrollBar.style.height =
            this.itemHeight * this.list.length + 'px';
        },
        methods: {
          scrollListener: _.throttle(function () {
            console.log('-----');
            //获取滚动高度
            let scrollTop = this.$refs.listWrap.scrollTop;
            //开始的数组索引
            this.start = Math.floor(scrollTop / this.itemHeight);
            //结束索引
            this.end = this.start + this.showNum;
            //绝对定位对相对定位的偏移量
            this.$refs.list.style.top = scrollTop + 'px';
          }, 100),
        },
      });
    </script>
  </body>
</html>
